package com.toptech.downloadmanager.download;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.widget.Toast;

import com.toptech.downloadmanager.FileInfo;
import com.toptech.downloadmanager.TaskInfo;
import com.toptech.downloadmanager.ToolUtils.ContantsUtils;
import com.toptech.downloadmanager.ToolUtils.lg;
import com.toptech.downloadmanager.db.DBThreadListener;
import com.toptech.downloadmanager.db.DBThreadListenerImp;
import com.toptech.downloadmanager.entity.ThreadInfo;

import org.litepal.crud.DataSupport;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by zhengshaorui on 2017/2/22.
 */

public class DownloadTask {
    private static final String TAG = "DownloadTask";
    private Context mContext;
    private long downloadlength = 0;
    private long lastDownloadlength = 0;
    private FileInfo mFileInfo;
    public  volatile boolean isPause = false;
    public  volatile boolean isTaskDowning = false;
    public  volatile boolean isDelete = false;

    public  ExecutorService mExecutor ;
    private static final int FILE_ERROR = 2;

    Handler mDeliver = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == FILE_ERROR){
                String error_msg = (String) msg.obj;
                if (error_msg.contains("SocketTimeoutException")) {
                    //启动多个线程
                   /* for (ThreadInfo info : threads) {
                        DownloadThread downloadThread = new DownloadThread(info);
                        // downloadThread.start();
                        sExecutorService.execute(downloadThread);


                        mThreadList.add(downloadThread);
                    }*/
                }

            }
        }
    };





    private List<DownloadThread> mThreadList ;

   // private DBThreadListener mDbThreadListener;
    public DownloadTask(Context mContext,int threadcount) {
        this.mContext = mContext;
       // mDbThreadListener = new DBThreadListenerImp(mContext);
        mExecutor = Executors.newFixedThreadPool(threadcount);
    }


    public void download(FileInfo fileInfo,DownloadCallback callback){
        mFileInfo = fileInfo;
       // List<ThreadInfo> threads = mDbThreadListener.getThreads(fileInfo.getApkurl());
        List<ThreadInfo> threads = DataSupport.where("url = ?",fileInfo.getApkurl()).find(ThreadInfo.class);
        lg.d("fileinfo: "+fileInfo);
        if (threads.isEmpty()){
            //由于是多线程，所以，我们在这里应该重新对 threadinfo 处理

            //分成很多小块来下载,用线程数的平方，假如有5个线程，则一个文件分成25块来下载
            //如果修改这里，请把data/data/com.toptech.downloadmanager/databases 下的数据库也删了
            int count = mFileInfo.getThreadCount() *3;

            long threadLength = mFileInfo.getLength() / count; //每个线程的大小
            String url = mFileInfo.getApkurl();

            for (int i = 0; i < count; i++) {
                ThreadInfo threadInfo = new ThreadInfo(i,url,
                        i*threadLength,(i+1)*threadLength-1,0,0);

                if (i == count - 1){ //最后一个除不尽，则end用文件长度来实现
                    threadInfo.setEnd(mFileInfo.getLength());
                }

                threads.add(threadInfo); //把创建的集合添加到 list 里面
                //开始之前先检测是否数据库有线程信息
              //  mDbThreadListener.insertThread(threadInfo); //第一次的时候，把线程保存到数据库
                threadInfo.save();
            }
        }

        //由于是多个线程，我们需要一个list管理起来
        mThreadList = new ArrayList<DownloadThread>();

        //启动多个线程
        for (ThreadInfo info : threads) {
            DownloadThread downloadThread = new DownloadThread(info,callback);
            mExecutor.execute(downloadThread);

            mThreadList.add(downloadThread);
        }
    }


    private synchronized  void checkThreadFinished(DownloadCallback callback){
        boolean allfinished = true;
        for(DownloadThread downloadThread : mThreadList){
            if (!downloadThread.isFinished){
                allfinished = false;
            }
        }
        callback.downloadStatus(mFileInfo.getApkurl(),ContantsUtils.FILE_LOADING);
        if (allfinished){
            callback.success(mFileInfo);
            callback.getDownloadInfo(mFileInfo);


            mFileInfo.setProgress(100);
            callback.downloadStatus(mFileInfo.getApkurl(),ContantsUtils.FILE_COMPLETED);
            float filesize = (float)mFileInfo.getLength()/1024/1024;

            TaskInfo taskInfo = new TaskInfo(0,mFileInfo.getFileName(),mFileInfo.getApkurl(),
               100,getFloatToString(filesize),getFloatToString(filesize),
                    mFileInfo.getThreadCount(),mFileInfo.getPriority());
            taskInfo.setFileprogress(100);  //把进度条更新进去
            taskInfo.saveOrUpdate("fileurl = ?",taskInfo.getFileurl());

            DataSupport.deleteAll(ThreadInfo.class);
            Log.d(TAG, mFileInfo.getFileName()+"下载完成");

        }
    }

    class DownloadThread extends Thread{
        ThreadInfo mThreadInfo;
        public boolean isFinished = false;
        DownloadCallback mCallback;
        public DownloadThread(ThreadInfo threadinfo,DownloadCallback callback) {
            this.mThreadInfo = threadinfo;
            this.mCallback = callback;
           // Log.d(TAG, "DownloadThread: "+threadinfo);
        }


        @Override
        public void run() {
            super.run();
            HttpURLConnection conn = null;
            RandomAccessFile raf = null;
            BufferedInputStream bis = null;
            //
            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

            try {

            // Log.d(TAG, "run: "+mThreadInfo);
                URL url = new URL(mThreadInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                //加上这句是为了防止connection.getContentLength()获取不到
                conn.setRequestProperty(
                        "Accept",
                        "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " +
                                "application/x-shockwave-flash, application/xaml+xml, " +
                                "application/vnd.ms-xpsdocument, application/x-ms-xbap, " +
                                "application/x-ms-application, application/vnd.ms-excel, " +
                                "application/vnd.ms-powerpoint, application/msword, */*");
                conn.setRequestProperty("Accept-Language", "zh-CN");
                conn.setRequestProperty("Charset", "UTF-8");
                conn.setRequestProperty("Accept-Encoding", "identity");
                conn.setRequestMethod("GET");
                conn.setReadTimeout(20000);
                conn.setConnectTimeout(20000);


                //设置下载位置
                long start = mThreadInfo.getStart()+mThreadInfo.getFinished(); //起始位置
                long end = mThreadInfo.getEnd(); //结束位置
                conn.setRequestProperty("Range", "bytes="+start+"-"+end);
                //保存整个文件的大小,在这过程中，如果线程过多，那么这个累加的过程就会比较慢
                downloadlength += mThreadInfo.getFinished();


                //文件写入位置
                File file = new File(mFileInfo.getDir(),mFileInfo.getFileName());
                raf = new RandomAccessFile(file,"rwd");
                raf.seek(start);

                int len = -1;
                byte[] bytes = new byte[1024*4];
                bis = new BufferedInputStream(conn.getInputStream());
                long time = System.currentTimeMillis();



                while( (len = bis.read(bytes)) != -1 ){
                    raf.write(bytes,0,len);
                    downloadlength += len; //
                    //保存每个线程的大小
                    mThreadInfo.setFinished(mThreadInfo.getFinished() + len);
                    isTaskDowning = true;  //表示任务正在下载
                    if (isPause) {
                        //数据库保存每段线程的断点位置
                       /* mDbThreadListener.updateThread(mThreadInfo.getUrl(), mThreadInfo.getId(),
                                mThreadInfo.getFinished());*/
                        mThreadInfo.saveOrUpdate("url = ? and id = ?",mThreadInfo.getUrl(),mThreadInfo.getId()+"");

                        isTaskDowning = false;  //按了暂停，线程停止，那么这个任务也不再下载了

                        //保存任务下载进度,只要在暂停和完成的时候，保存数据即可
                        int progress = (int) (downloadlength*100/mFileInfo.getLength());
                        float downloadsize = (float)downloadlength/1024/1024;
                        float filesize = (float)mFileInfo.getLength()/1024/1024;

                        TaskInfo taskInfo = new TaskInfo(0,mFileInfo.getFileName(),
                                mFileInfo.getApkurl(),progress,getFloatToString(downloadsize),
                                getFloatToString(filesize),
                                mFileInfo.getThreadCount(),mFileInfo.getPriority());
                        taskInfo.saveOrUpdate("fileurl = ?",taskInfo.getFileurl());
                      //  sCallback.downloadStatus(mFileInfo.getApkurl(),ContantsUtils.FILE_PAUSE);
                        return;
                    }

                    if (isDelete){
                     //   mDbThreadListener.deleteThread(mThreadInfo.getUrl());
                        DataSupport.deleteAll(ThreadInfo.class);
                        isTaskDowning = false;
                        return;
                    }
                    long currenttime = System.currentTimeMillis();

                    if (currenttime - time > 1000){
                        //下载速度好像有问题
                        float speed = (float) ((downloadlength-lastDownloadlength)*1000)/(currenttime - time);
                        time = currenttime;
                        lastDownloadlength = downloadlength;  //保存上一次的大小

                        mFileInfo.setSpeed(speed); //保存下载的平均速度
                        int progress = (int) (downloadlength*100/mFileInfo.getLength());
                        float downloadsize = (float)downloadlength/1024/1024;
                        float filesize = (float)mFileInfo.getLength()/1024/1024;
                        mFileInfo.setProgress(progress);
                        mFileInfo.setFiledownsize(getFloatToString(downloadsize));
                        mFileInfo.setFilesize(getFloatToString(filesize));

                        Log.d(TAG, "progress: "+progress);
                        mCallback.getDownloadInfo(mFileInfo);
                        
                        if (progress > 100) {
                            Toast.makeText(mContext, "出错了", Toast.LENGTH_SHORT).show();
                            return; 
                            
                        }


                  //  Log.d(TAG, "progress: "+mFileInfo.getFileName()+" "+(downloadlength*100/mFileInfo.getLength()));
                    }

                    //以百分比的形式传递
                }

                isFinished = true;//表示这个线程下载完成了
                checkThreadFinished(mCallback);

            } catch (Exception e) {
                e.printStackTrace();
                Log.d(TAG, "downloaderror: "+e.toString());
                //出错了，那么这两个参数也有刷新
                isPause = true;
                isTaskDowning = false;
                //mDeliver.obtainMessage(FILE_ERROR,e.toString()).sendToTarget();
                mFileInfo.setBtnstatus(ContantsUtils.PAUSE);
                int errorCode = ContantsUtils.ERROR_HTTP;
                if (e.toString().contains("already-closed")) //数据库问题
                    errorCode = ContantsUtils.ERROR_SQLITE;
                if (e.toString().contains("FileNotFoundException"))
                    errorCode = ContantsUtils.ERROR_FILE;
                DownloadTaskManger.Holder.getInstance().
                        mDownloadCallback.failed(errorCode,e.toString(),mFileInfo);

            }finally {
                if (bis != null) try {
                    bis.close();
                    if (raf != null) raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (conn != null) conn.disconnect();
            }

        }
    }


    private String getFloatToString(Float value){
        DecimalFormat decimalFormat =
                new DecimalFormat("0.00");//构造方法的字符格式这里如果小数不足2位,会以0补足.
        return  decimalFormat.format(value);
    }

    public void pauseDownload(){
        isPause = true;
    }

    public void deleteDownload(){
        isDelete = true;
    }
}
